package community.hospital.aspect;

import community.hospital.annotation.CacheQuery;
import community.hospital.annotation.CacheRemove;
import community.hospital.annotation.CacheUpdate;
import community.hospital.convert.UserConvert;
import community.hospital.entity.User;
import community.hospital.entity.response.UserResponse;
import community.hospital.service.domain.UserService;
import community.hospital.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static community.hospital.common.RedisConstant.*;

/**
 * @Author：ydj
 * @Date：2023/1/21 19:37
 */
@Aspect
@Component
@Slf4j
public class CacheAspect {
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private UserService userService;
    private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();


    @After("@annotation(cacheRemove)")
    public void cacheRemoveAspect(JoinPoint joinPoint, CacheRemove cacheRemove) {
        String[] values = cacheRemove.value();
        String key = cacheRemove.key();
        Object v = getValueBySpel(key, joinPoint);
        List<String> keys = new ArrayList<>();
        for (String value : values) {
            if (v instanceof List) {
                keys.addAll(((List<?>) v).stream().map(v1-> value +SPLIT+ v1).collect(Collectors.toList()));
            } else{
                keys.add(value + SPLIT+ v);
            }
        }
        redisUtil.del(keys);
    }

    @After("@annotation(cacheUpdate)")
    public void cacheUpdateAspect(JoinPoint joinPoint, CacheUpdate cacheUpdate) {
        String[] values = cacheUpdate.value();
        String key = cacheUpdate.key();
        Object v = getValueBySpel(key, joinPoint);
        User user = userService.getById(((Long) v));
        UserResponse userResponse = UserConvert.INSTANCE.convertToResp(user);
        ArrayList<String> keys = new ArrayList<>();
        for (String value : values) {
            keys.add(value + SPLIT + key);
        }
        redisUtil.del(keys);
        for (String k : keys) {
            if (k.contains(LOGIN_USER)) {
                redisUtil.set(k, userResponse, USER_PERSISTENCE_TIME, DEFAULT_TIME_UNIT);
            } else {
                redisUtil.set(key,userResponse,DEFAULT_CACHE_TIME, DEFAULT_TIME_UNIT);
            }
        }
    }

    @Around(value = "@annotation(cacheQuery)")
    public Object cacheQueryAspect(ProceedingJoinPoint pjp, CacheQuery cacheQuery) {

        String value = cacheQuery.value();
        Object v = getValueBySpel(cacheQuery.key(), pjp);
        String redisKey = value + SPLIT + v;
        return redisUtil.get(redisKey, () -> {
            Object result = null;
            try {
                result = pjp.proceed();
            } catch (Throwable e) {
                e.printStackTrace();
            }
            return result;
        }, DEFAULT_CACHE_TIME);

    }
    private Object getValueBySpel(String spel,JoinPoint joinPoint) {
        EvaluationContext context = new StandardEvaluationContext();
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] parameterNames = signature.getParameterNames();
        for (int i = args.length - 1; i >= 0; i--) {
            context.setVariable(parameterNames[i], args[i]);
        }
        Expression expression = spelExpressionParser.parseExpression(spel);
        return expression.getValue(context);
    }
}
